package org.iamd.domain.business;

import java.util.ArrayList;
import java.util.List;

import org.iamd.domain.data.Data;
import org.iamd.domain.data.DataAccess;
import org.iamd.util.CommonMsgKeys;

/**
 * Основен обект за търсене на Бизнес обекти. Предоставя базова функционалност.
 * 
 * @author И. Атанасов
 * 
 * @param <D>
 *            Data обектите за работата на Бизнес обектите и DataAccess слоя
 * @param <C>
 *            Тип критерии за търсене
 * @param <L>
 *            DataAccess слоя за търсенето
 * @param <B>
 *            Бизнес обектите, които се връщат като резултат от търсенето
 */
public abstract class SearchObject<D extends Data, C extends Object, L extends DataAccess<D, C>, B extends BusinessObject<D, L>>
		extends BusinessWorker<L> {

	/** Флаг за shallow или deep copy при инстанциране на бизнес обекта. */
	private boolean fullyLoad;

	/**
	 * @see BusinessWorker#BusinessWorker()
	 */
	protected SearchObject() throws ApplicationException {
		super();
	}

	/**
	 * Отваряне на транзакция, формиране на критерии за търсене, инстанциране на съответните бизнес
	 * обекти по намерените VO-та. Взима се под внимание флага {@link SearchObject#fullyLoad} за
	 * дълбочината на зареждане на обекта.
	 * 
	 * @return списък със заредени бизнес обекти
	 * 
	 * @throws ApplicationException
	 *             при неуспешно търсене
	 */
	public List<B> execute() throws ApplicationException {
		List<B> result = new ArrayList<B>();
		try {
			L dao = getDAO();
			dao.beginWork();
			for (D vo : getDAO().findByCriteria(buildCriteria())) {
				result.add(createResultObject(vo));
			}
			dao.commitWork();
		} catch (Exception e) {
			throw new ApplicationException(CommonMsgKeys.M0006_SEARCH_FAILED, e);
		}
		return result;
	}

	/**
	 * @return флаг за shallow или deep copy при инстанциране на бизнес обекта
	 */
	public boolean isFullyLoad() {
		return this.fullyLoad;
	}

	/**
	 * Setter за shallow или deep copy.
	 * 
	 * @param fullyLoad
	 *            флаг за shallow или deep copy
	 */
	public void setFullyLoad(boolean fullyLoad) {
		this.fullyLoad = fullyLoad;
	}

	/**
	 * @return инстанция на празен критерий
	 */
	protected C buildCriteria() {
		return getDAO().createCriteria();
	}

	/**
	 * Конструира конкретен Бизнес обект по зададения data обект.
	 * 
	 * @param data
	 *            VO
	 * 
	 * @return нова инстанция на наследник на GeneralBusinessObject
	 * 
	 * @throws Exception
	 *             при възникнало изключение при инстанциране
	 */
	protected B createResultObject(D data) throws Exception {
		return getBOClazz().getConstructor(data.getClass(), Boolean.class).newInstance(data,
				this.fullyLoad);
	}

	/**
	 * @return класа на бизнес обекта, който трябва да се инстанцира
	 */
	protected abstract Class<? extends B> getBOClazz();

	/**
	 * Проверка за празен аргумент.
	 * 
	 * @param attr
	 *            аргумент подлежащ на проверка
	 * 
	 * @return <code>true</code> - ако аргумента е <code>null</code>, празен или 0,
	 *         <code>false</code> - в противен случай.
	 */
	protected final boolean isEmpty(Object attr) {
		return attr == null || attr.toString().trim().equals("") || attr.equals(0);
	}

}